#include "board_utils.h"
#include <iostream>
#include <ctime>
#include <iomanip>
#include <sstream>

// 单例实现
BoardUtils &BoardUtils::getInstance()
{
    static BoardUtils instance;
    return instance;
}

BoardUtils::BoardUtils()
{
    // 管理品台服务器
    http_client_ = std::make_unique<HttpClient>("8.152.4.127", 443);
    http_client_->setDefaultHeaders({{"User-Agent", "MyHttpsClient/1.0"},
                                     {"Accept", "application/json"}});
    http_client_->enable_server_certificate_verification(false);
    http_client_->setTimeout(5);
    http_client_->setFollowRedirect(true);

    // 安科服务器
    anke_http_client_ = std::make_unique<httplib::Client>("host.gymparty.net", 81);
    anke_http_client_->enable_server_certificate_verification(false);
    anke_http_client_->set_follow_location(true);
    anke_http_client_->set_default_headers({{"User-Agent", "MyHttpsClient/1.0"},
                                            {"Accept", "application/json"}});
    // 串口
    serial_ = std::make_unique<Serial>("/dev/ttyS6");
    serial_->open();
    // Socket客户端
    socket_client_ = std::make_unique<SocketClient>();
    socket_client_->setAutoReconnect(true, -1, 1000, 30000);

    // 设置连接状态回调
    socket_client_->setConnectionCallback([this](bool connected)
                                          {
        if (connected) {
            std::cout << "已连接到服务器" << std::endl;
        } else {
            std::cout << "与服务器的连接已断开，正在尝试重连..." << std::endl;
        } });

    // 设置消息回调
    socket_client_->setMessageCallback([&, this](const std::string &message)
                                       {
        // 原有的消息处理代码...
        std::cout << "服务器消息: " << message << std::endl;

        // 简单解析JSON格式消息: {"controller_id":"1","lock_numble":"1"}
        std::string controller_id_key = "\"controller_id\":\"";
        std::string lock_numble_key = "\"lock_numble\":\"";

        size_t controller_pos = message.find(controller_id_key);
        size_t lock_pos = message.find(lock_numble_key);

        if (controller_pos != std::string::npos && lock_pos != std::string::npos) {
            // 解析逻辑...
            controller_pos += controller_id_key.length();
            lock_pos += lock_numble_key.length();

            size_t controller_end = message.find("\"", controller_pos);
            size_t lock_end = message.find("\"", lock_pos);

            if (controller_end != std::string::npos && lock_end != std::string::npos) {
                std::string controller_id_str = message.substr(controller_pos, controller_end - controller_pos);
                std::string lock_id_str = message.substr(lock_pos, lock_end - lock_pos);

                try {
                    int controller_id = std::stoi(controller_id_str);
                    int lock_id = std::stoi(lock_id_str);

                    std::cout << "开锁请求: 控制板 " << controller_id << ", 锁号 " << lock_id << std::endl;

                    // 调用开锁函数
                    bool result = serial_->openLock(controller_id, lock_id);
                    if (result) {
                        std::cout << "成功打开锁 " << lock_id << " 在控制板 " << controller_id << std::endl;
                    } else {
                        std::cerr << "打开锁 " << lock_id << " 在控制板 " << controller_id << " 失败" << std::endl;
                    }
                } catch (const std::exception &e) {
                    std::cerr << "转换ID为整数时出错: " << e.what() << std::endl;
                }
            }
        } else {
            std::cerr << "无效的消息格式" << std::endl;
        } });
}

BoardUtils::~BoardUtils() {}

Lock BoardUtils::httpDistributeLock(const std::string &brand_code, const std::string &organization,
                                    const std::string &zone, const std::string &phone, const std::string &type, const std::string &layer)
{
    std::string path = "/cabinet/locks/distribute";
    std::string body = "{";
    body += "\"brand_code\": \"" + brand_code + "\",";
    body += "\"organization\": \"" + organization + "\",";
    body += "\"zone\": \"" + zone + "\",";
    body += "\"phone\": \"" + phone + "\",";
    body += "\"type\": \"" + type + "\"";
    body += ",\"layer\": \"" + layer + "\"";
    body += "}";

    auto res = http_client_->post(path, body, "application/json");
    std::cout << "[httpDistributeLock] POST 请求体: " << res.body << std::endl;
    Lock lock;
    if (res.ok && res.status == 200)
    {
        std::string response = res.body;
        auto parseJsonField = [&response](const std::string &fieldName) -> std::string
        {
            std::string pattern = "\"" + fieldName + "\":\"";
            size_t start = response.find(pattern);
            if (start == std::string::npos)
                return "";
            start += pattern.length();
            size_t end = response.find("\"", start);
            if (end == std::string::npos)
                return "";
            return response.substr(start, end - start);
        };
        lock.brand_code = parseJsonField("brand_code");
        lock.collect_time = parseJsonField("collect_time");
        lock.controller_id = parseJsonField("controller_id");
        lock.lock = parseJsonField("lock");
        lock.lock_numble = parseJsonField("lock_numble");
        lock.organization = parseJsonField("organization");
        lock.person = parseJsonField("person");
        lock.phone = parseJsonField("phone");
        lock.status = parseJsonField("status");
        lock.type = parseJsonField("type");
        lock.use_time = parseJsonField("use_time");
        lock.zone = parseJsonField("zone");
        lock.layer = parseJsonField("layer");
        lock.cabinet_number = parseJsonField("cabinet_number");
        lock.effective_time = parseJsonField("effective_time");
    }
    else
    {
        lock.status = "error";
        lock.lock = "error";
    }
    return lock;
}

PersonConfig BoardUtils::httpGetPersonConfig(const std::string &brand_code, const std::string &organization,
                                             const std::string &zone, const std::string &phone)
{
    std::string path = "/cabinet/persons/query?brand_code=" + brand_code +
                       "&organization=" + organization +
                       "&phone=" + phone;

    auto res = http_client_->get(path);
    std::cout << "[httpGetPersonConfig] GET 请求体: " << res.body << std::endl;

    PersonConfig config;
    if (res.ok && res.status == 200)
    {
        try
        {
            auto arr = nlohmann::json::parse(res.body);
            if (arr.is_array() && !arr.empty())
            {
                const auto &obj = arr[0];
                config.person = obj.value("person", "");
                config.phone = obj.value("phone", "");
                config.type = obj.value("type", "");
                config.passwd = obj.value("passwd", "");
            }
        }
        catch (const std::exception &e)
        {
            std::cerr << "JSON解析失败: " << e.what() << std::endl;
        }
    }
    return config;
}

Lock BoardUtils::httpQueryLock(const std::string &brand_code, const std::string &organization,
                               const std::string &zone, const std::string &phone, const std::string &type)
{
    std::string path = "/cabinet/locks/query?brand_code=" + brand_code +
                       "&organization=" + organization +
                       "&zone=" + zone +
                       "&phone=" + phone +
                       "&type=" + type;

    auto res = http_client_->get(path);
    std::cout << "[httpQueryLock] POST 请求体: " << res.body << std::endl;

    Lock lock;
    if (res.ok && res.status == 200)
    {
        std::string response = res.body;
        auto parseJsonField = [&response](const std::string &fieldName) -> std::string
        {
            std::string pattern = "\"" + fieldName + "\":\"";
            size_t start = response.find(pattern);
            if (start == std::string::npos)
                return "";
            start += pattern.length();
            size_t end = response.find("\"", start);
            if (end == std::string::npos)
                return "";
            return response.substr(start, end - start);
        };
        lock.brand_code = parseJsonField("brand_code");
        lock.collect_time = parseJsonField("collect_time");
        lock.controller_id = parseJsonField("controller_id");
        lock.lock = parseJsonField("lock");
        lock.lock_numble = parseJsonField("lock_numble");
        lock.organization = parseJsonField("organization");
        lock.person = parseJsonField("person");
        lock.phone = parseJsonField("phone");
        lock.status = parseJsonField("status");
        lock.type = parseJsonField("type");
        lock.use_time = parseJsonField("use_time");
        lock.layer = parseJsonField("layer");
        lock.cabinet_number = parseJsonField("cabinet_number");
        lock.effective_time = parseJsonField("effective_time");
    }
    return lock;
}

Lock BoardUtils::httpReturnLock(const std::string &brand_code, const std::string &organization,
                                const std::string &zone, const std::string &phone, const std::string &type)
{
    std::string path = "/cabinet/locks/return";
    std::string request_body = "{"
                               "\"brand_code\":\"" +
                               brand_code + "\","
                                            "\"organization\":\"" +
                               organization + "\","
                                              "\"zone\":\"" +
                               zone + "\","
                                      "\"phone\":\"" +
                               phone + "\","
                                       "\"type\":\"" +
                               type + "\""
                                      "}";

    auto res = http_client_->post(path, request_body);
    std::cout << "[httpReturnLock] POST 请求体: " << res.body << std::endl;

    Lock lock;
    if (res.ok && res.status == 200)
    {
        std::string response = res.body;
        auto parseJsonField = [&response](const std::string &fieldName) -> std::string
        {
            std::string pattern = "\"" + fieldName + "\":\"";
            size_t start = response.find(pattern);
            if (start == std::string::npos)
                return "";
            start += pattern.length();
            size_t end = response.find("\"", start);
            if (end == std::string::npos)
                return "";
            return response.substr(start, end - start);
        };
        lock.brand_code = parseJsonField("brand_code");
        lock.cabinet_number = parseJsonField("cabinet_number");
        lock.collect_time = parseJsonField("collect_time");
        lock.controller_id = parseJsonField("controller_id");
        lock.effective_time = parseJsonField("effective_time");
        lock.layer = parseJsonField("layer");
        lock.lock = parseJsonField("lock");
        lock.lock_numble = parseJsonField("lock_numble");
        lock.organization = parseJsonField("organization");
        lock.person = parseJsonField("person");
        lock.phone = parseJsonField("phone");
        lock.status = parseJsonField("status");
        lock.type = parseJsonField("type");
        lock.use_time = parseJsonField("use_time");
        lock.zone = parseJsonField("zone");
    }
    return lock;
}

int BoardUtils::httpGetAmountCabinetByLayer(const std::string &brand_code, const std::string &organization,
                                            const std::string &zone, const std::string &layer)
{
    std::string path = "/cabinet/locks/query?brand_code=" + brand_code +
                       "&organization=" + organization +
                       "&zone=" + zone +
                       "&layer=" + layer +
                       "&status=未使用";

    auto res = http_client_->get(path);
    std::cout << "[httpGetAmountCabinetByLayer] POST 请求体: " << res.body << std::endl;

    if (res.ok && res.status == 200)
    {
        int count = 0;
        std::string key = "\"lock_numble\":\"";
        size_t pos = 0;
        while ((pos = res.body.find(key, pos)) != std::string::npos)
        {
            count++;
            pos += key.length();
        }
        return count;
    }
    else
    {
        return 0;
    }
}

void BoardUtils::httpUploadLog(const std::string &brand_code, const std::string &organization,
                               const std::string &zone, const std::string &phone, const std::string &cabinet_number,
                               const std::string &cabinet_type, const std::string &action)
{
    std::time_t t = std::time(nullptr);
    std::tm tm_now;
    localtime_r(&t, &tm_now);
    std::ostringstream oss;
    oss << std::put_time(&tm_now, "%Y-%m-%d %H:%M:%S");
    std::string time = oss.str();

    std::string path = "/cabinet/records/add";
    std::string body = "{";
    body += "\"brand_code\": \"" + brand_code + "\",";
    body += "\"organization\": \"" + organization + "\",";
    body += "\"zone\": \"" + zone + "\",";
    body += "\"phone\": \"" + phone + "\",";
    body += "\"cabinet_number\": \"" + cabinet_number + "\",";
    body += "\"cabinet_type\": \"" + cabinet_type + "\",";
    body += "\"action\": \"" + action + "\",";
    body += "\"time\": \"" + time + "\"";
    body += "}";

    auto res = http_client_->post(path, body, "application/json");
    std::cout << "[httpUploadLog] POST 请求体: " << res.body << std::endl;
}

std::vector<Lock> BoardUtils::httpGetAllLocks(const std::string &brand_code, const std::string &organization,
                                              const std::string &zone)
{
    std::string path = "/cabinet/locks/query?brand_code=" + brand_code +
                       "&organization=" + organization +
                       "&zone=" + zone;

    auto res = http_client_->get(path);
    std::vector<Lock> locks;
    std::cout << "[httpGetAllLocks] POST 请求体: " << res.body << std::endl;

    if (res.ok && res.status == 200)
    {
        try
        {
            auto arr = nlohmann::json::parse(res.body);
            if (!arr.is_array())
                return locks;

            for (const auto &obj : arr)
            {
                Lock lock;
                lock.brand_code = obj.value("brand_code", "");
                lock.cabinet_number = obj.value("cabinet_number", "");
                lock.collect_time = obj.value("collect_time", "");
                lock.controller_id = obj.value("controller_id", "");
                lock.effective_time = obj.value("effective_time", "");
                lock.layer = obj.value("layer", "");
                lock.lock = obj.value("lock", "");
                lock.lock_numble = obj.value("lock_numble", "");
                lock.organization = obj.value("organization", "");
                lock.person = obj.value("person", "");
                lock.phone = obj.value("phone", "");
                lock.status = obj.value("status", "");
                lock.type = obj.value("type", "");
                lock.use_time = obj.value("use_time", "");
                lock.zone = obj.value("zone", "");
                locks.push_back(lock);
            }

            // 写入 lock_list.txt，只保存cabinet_number,controller_id,lock_numble
            std::ofstream file("lock_list.txt");
            if (file.is_open())
            {
                for (const auto &lock : locks)
                {
                    file << lock.cabinet_number << "," << lock.controller_id << "," << lock.lock_numble << "\n";
                }
                file.close();
            }
        }
        catch (const std::exception &e)
        {
            std::cerr << "JSON解析失败: " << e.what() << std::endl;
        }
    }
    return locks;
}

DeviceConfig BoardUtils::loadDeviceConfig(const std::string &txtfile)
{
    DeviceConfig config;
    std::ifstream file(txtfile);
    if (!file.is_open())
    {
        std::cerr << "设备配置文件不存在或无法打开!" << std::endl;
        return config;
    }

    std::string line;
    while (std::getline(file, line))
    {
        auto pos = line.find('=');
        if (pos == std::string::npos)
            continue;
        std::string key = line.substr(0, pos);
        std::string value = line.substr(pos + 1);

        if (key == "brand_code")
            config.brand_code = value;
        else if (key == "organization")
            config.organization = value;
        else if (key == "zone")
            config.zone = value;
        else if (key == "sn_code")
            config.sn_code = value;
        else if (key == "device_type")
            config.device_type = value;
        else if (key == "auth_enabled")
            config.auth_enabled = (value == "1" || value == "true");
        else if (key == "card_enabled")
            config.card_enabled = (value == "1" || value == "true");
        else if (key == "palm_enabled")
            config.palm_enabled = (value == "1" || value == "true");
        else if (key == "face_enabled")
            config.face_enabled = (value == "1" || value == "true");
    }
    file.close();
    return config;
}

bool BoardUtils::httpGetZoneAuthEnabled(const std::string &brand_code, const std::string &organization, const std::string &zone)
{
    std::string path = "/cabinet/zones/query?brand_code=" + brand_code +
                       "&organization=" + organization +
                       "&zone=" + zone;

    auto res = http_client_->get(path);
    std::cout << "[httpGetZoneAuthEnabled] GET 请求体: " << res.body << std::endl;

    if (res.ok && res.status == 200)
    {
        try
        {
            auto arr = nlohmann::json::parse(res.body);
            if (arr.is_array() && !arr.empty())
            {
                const auto &obj = arr[0];
                std::string auth_enabled = obj.value("auth_enabled", "");
                return (auth_enabled == "1" || auth_enabled == "true");
            }
        }
        catch (const std::exception &e)
        {
            std::cerr << "JSON解析失败: " << e.what() << std::endl;
        }
    }
    return false;
}

std::string BoardUtils::httpGetContractId(const std::string &brand_code, const std::string &club_id,
                                          const std::string &key, const std::string &phone)
{
    std::string path = "/Vein/SCHWebService.asmx/GetContractInfo";
    httplib::Headers headers = {
        {"User-Agent", "MyHttpsClient/1.0"},
        {"Accept", "application/json"},
    };
    httplib::Params params = {
        {"BrandCode", brand_code},
        {"ClubID", club_id},
        {"Mobile", phone},
        {"CardNo", ""},
        {"Key", key}};

    auto res = anke_http_client_->Post(path.c_str(), headers, params);
    std::cout << "[httpGetContractId] GET 请求体: " << res->body << std::endl;
    if (res && res->status == 200)
    {
        std::string body = res->body;
        // 提取 XML 中的 JSON 字符串
        std::smatch match;
        std::regex re(R"(<string[^>]*>(.*)</string>)");
        if (std::regex_search(body, match, re) && match.size() > 1)
        {
            std::string jsonStr = match[1].str();
            try
            {
                auto arr = nlohmann::json::parse(jsonStr);
                if (arr.is_array() && !arr.empty())
                {
                    const auto &obj = arr[0];
                    return obj.value("ContractID", "");
                }
            }
            catch (const std::exception &e)
            {
                std::cerr << "JSON解析失败: " << e.what() << std::endl;
            }
        }
    }
    return "";
}

OrgConfig BoardUtils::httpGetOrgConfig(const std::string &brand_code, const std::string &organization)
{
    OrgConfig config;
    std::string path = "/cabinet/organizations/query?brand_code=" + brand_code +
                       "&organization=" + organization;

    auto res = http_client_->get(path);
    std::cout << "[httpGetOrgConfig] GET 请求体: " << res.body << std::endl;

    if (res.ok && res.status == 200)
    {
        try
        {
            auto arr = nlohmann::json::parse(res.body);
            if (arr.is_array() && !arr.empty())
            {
                const auto &obj = arr[0];
                config.organization = obj.value("organization", "");
                config.club_id = obj.value("club_id", "");
                config.key = obj.value("key", "");
            }
        }
        catch (const std::exception &e)
        {
            std::cerr << "JSON解析失败: " << e.what() << std::endl;
        }
    }
    return config;
}

std::string BoardUtils::httpCheckIn(const std::string &brand_code, const std::string &club_id,
                                    const std::string &contract_id, const std::string &key,
                                    const std::string &phone, const std::string &identity_type,
                                    const std::string &device_name)
{
    std::string path = "/Vein/SCHWebService.asmx/CheckIn";
    httplib::Headers headers = {
        {"User-Agent", "MyHttpsClient/1.0"},
        {"Accept", "application/json"},
    };
    httplib::Params params = {
        {"BrandCode", brand_code},
        {"ClubID", club_id},
        {"ContractID", contract_id},
        {"Key", key},
        {"Mobile", phone},
        {"IdentityType", identity_type},
        {"DeviceName", device_name}};

    auto res = anke_http_client_->Post(path.c_str(), headers, params);
    std::cout << "[httpCheckIn] GET 请求体: " << res->body << std::endl;
    int code = 0;
    if (res && res->status == 200)
    {
        std::string body = res->body;
        std::smatch match;
        std::regex re(R"(<int[^>]*>(-?\d+)</int>)");
        if (std::regex_search(body, match, re) && match.size() > 1)
        {
            code = std::stoi(match[1].str());
        }
    }

    std::string error_message;
    switch (code)
    {
    case 1:
        error_message = "成功";
        break;
    case -1:
        error_message = "系统错误";
        break;
    case -2:
        error_message = "参数错误";
        break;
    case -3:
        error_message = "此卡为外店卡，不能在本店通店使用";
        break;
    case -4:
        error_message = "剩余次数为0";
        break;
    case -5:
        error_message = "已超过本会员可携带人数";
        break;
    case -6:
        error_message = "主卡不在场，副卡无法入场";
        break;
    case -7:
        error_message = "高峰时段，目前为禁止入场时段，无法入场";
        break;
    case -8:
        error_message = "月利用次数已用完，无法入场";
        break;
    case -9:
        error_message = "年利用次数已用完，无法入场";
        break;
    case -10:
        error_message = "月利用天数已用完，无法入场";
        break;
    case -11:
        error_message = "入场类商品不存在，无法入场";
        break;
    case -12:
        error_message = "储值卡余额不足，无法入场";
        break;
    case -13:
        error_message = "没有权限，无法通过设备";
        break;
    case -14:
        error_message = "课程已过期，无法入场";
        break;
    case -15:
        error_message = "已无剩余课时，无法入场";
        break;
    case -17:
        error_message = "没有开卡，无法入场";
        break;
    case -18:
        error_message = "此会员已出相同时间内的其他但仍有效的年/月卡会籍或其他有效的年/月卡会籍未开卡";
        break;
    case -19:
        error_message = "您没有预约课程，禁止入场！";
        break;
    case -21:
        error_message = "该会员在黑名单中，不能入场！";
        break;
    default:
        error_message = "未知错误";
        break;
    }
    return error_message;
}


void BoardUtils::httpGetQrCode(const std::string &brand_code, const std::string &env_version, const std::string &filename)
{
    std::string path = "/cabinet/wx/qrcode?brand_code=" + brand_code + "&env_version=" + env_version;

    auto res = http_client_->get(path);
    if (res.ok)
    {
        std::ofstream fout(filename, std::ios::binary);
        fout.write(res.body.data(), res.body.size());
        fout.close();
        std::cout << "二维码已保存为 " << filename << std::endl;
    }
    else
    {
        std::cerr << "二维码请求错误: " << res.error << std::endl;
    }
}